home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / develop / develop issue 24 / develop issue 24 code / scriptable database 1.0a15.sea / Scriptable Database 1.0a15 / Database / DBElement.cp / DBElement.cp
Encoding:
Text File  |  1996-04-29  |  13.7 KB  |  399 lines  |  [TEXT/CWIE]

  1. //================================================================================
  2. // Greg Anderson
  3. // db+
  4. //
  5. // Cursor to an object record
  6. // 16 May 1994
  7. // 31 Dec 1994
  8. //================================================================================
  9.  
  10. #include "DBElement.h"
  11.  
  12. #include "DBProperty.h"
  13. #include "Transaction.h"
  14.  
  15. #include "DatabaseDocument.h"
  16. #include "Exceptions.h"
  17.  
  18. #define INHERITED TDBRecord
  19.  
  20. //--------------------------------------------------------------------------------
  21. // TDBElement::~TDBElement
  22. //--------------------------------------------------------------------------------
  23. TDBElement::~TDBElement()
  24. {
  25. } // TDBElement::~TDBElement
  26.  
  27. //--------------------------------------------------------------------------------
  28. // TDBElement::CompareSortKeys
  29. //--------------------------------------------------------------------------------
  30. CompareEnumeration TDBElement::CompareSortKeys(TTransaction* t, AConst<TDBRecord> secondObject) const
  31. {
  32.     CompareEnumeration testResult;
  33.  
  34.     AConst<TDBProperty> nameCursor = this->DBElementRecord()->GetPropertyRecord(t, kNameProperty);
  35.     AConst<TDBProperty> secondName = secondObject->DBElementCursor()->GetPropertyRecord(t, kNameProperty);
  36.     if(nameCursor.Exists() && secondName.Exists())
  37.     {
  38.         testResult = nameCursor->Compare(t, secondName);
  39.     }
  40.     //
  41.     // If either item is empty, then the larger is the one
  42.     // that has data.  They are equal if neither has data.
  43.     //
  44.     else
  45.         testResult = (CompareEnumeration)(secondName.Exists() - nameCursor.Exists());
  46.         
  47.     return testResult;
  48. } // TDBElement::CompareSortKeys
  49.  
  50. //--------------------------------------------------------------------------------
  51. // TDBElement::RecordCanHaveElements
  52. //--------------------------------------------------------------------------------
  53. Boolean TDBElement::RecordCanHaveElements(TTransaction*) const
  54. {
  55.     return true;
  56. } // TDBElement::RecordCanHaveElements
  57.  
  58. //--------------------------------------------------------------------------------
  59. // TDBElement::RecordCanHaveProperties
  60. //--------------------------------------------------------------------------------
  61. Boolean TDBElement::RecordCanHaveProperties(TTransaction*) const
  62. {
  63.     return true;
  64. } // TDBElement::RecordCanHaveProperties
  65.  
  66. //--------------------------------------------------------------------------------
  67. // TDBElement::GetPropertyRecord
  68. //--------------------------------------------------------------------------------
  69. AConst<TDBProperty> TDBElement::GetPropertyRecord(TTransaction* t, long propertyID) const
  70. {
  71.     TDataRecordComparisonObject searchObject(propertyID);
  72.     AConst<TDBProperty> theProperty;
  73.     
  74.     AConst<TDBProperty> topProperty = this->Properties(t);
  75.     if(topProperty.Exists())
  76.     {
  77.         //
  78.         // We call 'FindItemInSubTree' instead of 'FindItemInTree' because
  79.         // we know we're already at the top of the property tree
  80.         //
  81.         AConst<TDBRecord> tempProperty = topProperty->FindItemInSubTree(t, &searchObject);
  82.         if(tempProperty.Exists())
  83.             theProperty = tempProperty->DBPropertyCursor();
  84.     }
  85.         
  86.     return theProperty;
  87. } // TDBElement::GetPropertyRecord
  88.  
  89. //--------------------------------------------------------------------------------
  90. // TDBElement::GetNamedElement
  91. //--------------------------------------------------------------------------------
  92. AConst<TDBElement> TDBElement::GetNamedElement(TTransaction* t, const TAbstractDataReference& nameSpec) const
  93. {
  94.     TObjectRecordComparisonObject searchObject(nameSpec);
  95.     AConst<TDBElement> theElement;
  96.     
  97.     AConst<TDBElement> topElement = this->Elements(t);
  98.     if(topElement.Exists())
  99.     {
  100.         //
  101.         // We call 'FindItemInSubTree' instead of 'FindItemInTree' because
  102.         // we know we're already at the top of the element tree
  103.         //
  104.         AConst<TDBRecord> tempElement = topElement->FindItemInSubTree(t, &searchObject);
  105.         if(tempElement.Exists())
  106.             theElement = tempElement->DBElementCursor();
  107.     }
  108.     
  109.     return theElement;
  110. } // TDBElement::GetNamedElement
  111.  
  112. //--------------------------------------------------------------------------------
  113. // TDBElement::GetLongwordProperty
  114. //--------------------------------------------------------------------------------
  115. long TDBElement::GetLongwordProperty(TTransaction* t, long propertyID) const
  116. {
  117.     AConst<TDBProperty> dataCursor = this->GetPropertyRecord(t, propertyID);
  118.     long result = 0;
  119.     if(dataCursor.Exists())
  120.     {
  121.         result = dataCursor->GetLongData(t);
  122.     }
  123.     // ••• fail if does not exist
  124.     return result;
  125. } // TDBElement::GetLongwordProperty
  126.  
  127. //--------------------------------------------------------------------------------
  128. // TDBElement::GetTypedProperty
  129. //--------------------------------------------------------------------------------
  130. void TDBElement::GetTypedProperty(TTransaction* t, long propertyID, TUpdataDataReference& data) const
  131. {
  132.     AConst<TDBProperty> dataCursor = this->GetPropertyRecord(t, propertyID);
  133.     if(dataCursor.Exists())
  134.     {
  135.         dataCursor->GetTypedData(t, data);
  136.     }
  137.     // ••• fail if does not exist
  138. } // TDBElement::GetTypedProperty
  139.  
  140. //--------------------------------------------------------------------------------
  141. // TDBElement::Verify
  142. //--------------------------------------------------------------------------------
  143. void TDBElement::Verify(TTransaction* t, Boolean verifyDeep /* = false */) const
  144. {
  145.     //
  146.     // Check properties and elements
  147.     //
  148.     AConst<TDBElement> elements = this->Elements(t);
  149.     if(elements.Exists())
  150.     {
  151.         if(elements->LiteralParentSibling(t)->RecordIndex() != this->RecordIndex())
  152.             DebugStr("\pTDBElement::Verify top element doesn't point back");
  153.     }
  154.     AConst<TDBProperty> properties = this->Properties(t);
  155.     if(properties.Exists())
  156.     {
  157.         if(properties->LiteralParentSibling(t)->RecordIndex() != this->RecordIndex())
  158.             DebugStr("\pTDBElement::Verify top property doesn't point back");
  159.     }
  160.     
  161.     INHERITED::Verify(t, verifyDeep);
  162.     
  163.     if(verifyDeep)
  164.     {
  165.         if(elements.Exists())
  166.             elements->Verify(t, true);
  167.         if(properties.Exists())
  168.             properties->Verify(t, true);
  169.     }
  170. } // TDBElement::Verify
  171.  
  172. //--------------------------------------------------------------------------------
  173. // TDBElement::InitializeNewRecord
  174. //--------------------------------------------------------------------------------
  175. void TDBElement::InitializeNewRecord(TTransaction* t)
  176. {
  177.     INHERITED::InitializeNewRecord(t);
  178.  
  179.     this->SetRecordFlags(t, kInitialObjectRecordFlags);
  180.     this->SetParentElementIndex(t, kNilIndex);
  181.     this->SetTopChildIndex(t, kNilIndex);
  182.     this->SetTopPropertyIndex(t, kNilIndex);
  183.     this->SetNamePropertyIndex(t, kNilIndex);
  184. } // TDBElement::InitializeNewRecord
  185.  
  186. //--------------------------------------------------------------------------------
  187. // TDBElement::PropertyValueChanged
  188. //
  189. // Whenever a property changes value, it informs the record that owns it by
  190. // calling this method.
  191. //--------------------------------------------------------------------------------
  192. void TDBElement::PropertyValueChanged(TTransaction* t, AConst<TDBProperty> propertyThatChanged)
  193. {
  194.     //
  195.     // If our name changes, then resort this record within the tree it's stored in.
  196.     // Note that we will also get notification when our name is removed from the
  197.     // tree, so we need to check for that case as well.
  198.     //
  199.     if((propertyThatChanged->PropertyID(t) == kNameProperty) && (propertyThatChanged->RecordIsInATree(t)))
  200.     {
  201.         this->SetNamePropertyIndex(t, propertyThatChanged->RecordIndex());
  202.         this->ResortThisRecord(t);
  203.     }
  204.     //
  205.     // If the property that changed was our cached name property,
  206.     // but its property ID is no longer kNameProperty or it is no
  207.     // longer in our property tree, then clear our name property
  208.     // cache entry.
  209.     //
  210.     else if(propertyThatChanged->RecordIndex() == this->NamePropertyIndex(t))
  211.     {
  212.         this->SetNamePropertyIndex(t, kNilIndex);
  213.         this->ResortThisRecord(t);
  214.     }
  215. } // TDBElement::PropertyValueChanged
  216.  
  217. //--------------------------------------------------------------------------------
  218. // TDBElement::GetPropertyRecordForceCreate
  219. //--------------------------------------------------------------------------------
  220. AConst<TDBProperty> TDBElement::GetPropertyRecordForceCreate(TTransaction* t, long propertyID)
  221. {
  222.     AConst<TDBProperty> theProperty = GetPropertyRecord(t, propertyID);
  223.     
  224.     //
  225.     // If 'forceCreate' is true and we could not find the property, then
  226.     // create it now and add it to the tree
  227.     //
  228.     if(theProperty.Exists() == false)
  229.     {
  230.         AnUpdate<TDBProperty> updateProperty = this->DBDocument()->NewDBProperty(t);
  231.         updateProperty->SetPropertyID(t, propertyID);
  232.         this->AddProperty(t, updateProperty);
  233.         
  234.         theProperty = AConst<TDBProperty>(updateProperty);
  235.         
  236.         //
  237.         // Cache the index of the name when it's created
  238.         //
  239.         if(propertyID == kNameProperty)
  240.         {
  241.             this->SetNamePropertyIndex(t, theProperty->RecordIndex());
  242.         }
  243.     }
  244.     
  245.     return theProperty;
  246. } // TDBElement::GetPropertyRecordForceCreate
  247.  
  248. //--------------------------------------------------------------------------------
  249. // TDBElement::AddElement
  250. //--------------------------------------------------------------------------------
  251. void TDBElement::AddElement(TTransaction* t, AnUpdate<TDBElement> newElement)
  252. {
  253.     AConst<TDBElement> topElement = this->Elements(t);
  254.     if(topElement.Exists())
  255.     {
  256.         (this->Transaction()->GetDBElementUpdatePointer(topElement))->Insert(t, newElement);
  257.     }
  258.     else
  259.     {
  260.         this->SetElements(t, newElement);
  261.         newElement->SetRecordIsAtTopOfTree(t, true);
  262.         newElement->SetParentSibling(t, this);
  263.     }
  264.  
  265.     //
  266.     // One of these days we should number the tree
  267.     //
  268.     newElement->SetNodeNumber(t, 0);
  269.     newElement->SetParentElement(t, this);
  270.     
  271. #ifdef SLOWDEBUG
  272.     newElement->Verify(t);
  273.     this->Verify(t, true);
  274. #endif
  275. } // TDBElement::AddElement
  276.  
  277. //--------------------------------------------------------------------------------
  278. // TDBElement::AddProperty
  279. //--------------------------------------------------------------------------------
  280. void TDBElement::AddProperty(TTransaction* t, AnUpdate<TDBProperty> newProperty)
  281. {
  282.     //
  283.     // Check to see if there's already a property with this ID
  284.     //
  285.     AConst<TDBProperty> existingProperty = this->GetPropertyRecord(t, newProperty->PropertyID(t));
  286.     Boolean addNewPropertyToTree = true;
  287.     
  288.     if(existingProperty.Exists())
  289.     {
  290.         //
  291.         // If the new property is already in the tree, then we
  292.         // don't really need to do anything else.
  293.         //
  294.         if(existingProperty->RecordIndex() == newProperty->RecordIndex())
  295.         {
  296.             addNewPropertyToTree = false;
  297.         }
  298.         //
  299.         // Delete the existing property if it isn't the same property
  300.         // that's already in the tree.
  301.         //
  302.         // NOTE:  It would be WAY better to copy the contents of
  303.         // "newProperty" into "existingProperty" and then free the
  304.         // new property; that way, properties that have been placed
  305.         // close to the record they are in will remain in their optimal
  306.         // position.  The problem with that technique is that it
  307.         // would require clients of "AddProperty" to take into account
  308.         // that "newProperty" might be deleted.
  309.         //
  310.         else
  311.         {
  312.             AnUpdate<TDBProperty> updateExistingProperty = this->Transaction()->GetDBPropertyUpdatePointer(existingProperty);
  313.             updateExistingProperty->RemoveFromTree(t);
  314.             updateExistingProperty->FreeThisRecord(t);
  315.         }
  316.     }
  317.         
  318.     //
  319.     // Add the new property to the tree after the old one's gone
  320.     //
  321.     if(addNewPropertyToTree == true)
  322.     {
  323.         AConst<TDBProperty> topProperty = this->Properties(t);
  324.         if(topProperty.Exists())
  325.         {
  326.             (this->Transaction()->GetDBPropertyUpdatePointer(topProperty))->Insert(t, newProperty);
  327.         }
  328.         else
  329.         {
  330.             this->SetProperties(t, newProperty);
  331.             newProperty->SetRecordIsAtTopOfTree(t, true);
  332.             newProperty->SetParentSibling(t, this);
  333.         }
  334.     }
  335.     
  336.     //
  337.     // Write in the index of this property if it's the "name" property
  338.     //
  339.     if(newProperty->PropertyID(t) == kNameProperty)
  340.     {
  341.         this->SetNamePropertyIndex(t, newProperty->RecordIndex());
  342.     }
  343.     
  344. #ifdef SLOWDEBUG
  345.     newProperty->Verify(t);
  346.     this->Verify(t, true);
  347. #endif
  348. } // TDBElement::AddProperty
  349.  
  350. //--------------------------------------------------------------------------------
  351. // TDBElement::AddLongwordProperty
  352. //--------------------------------------------------------------------------------
  353. void TDBElement::AddLongwordProperty(TTransaction* t, long propertyID, long newValue)
  354. {
  355.     AConst<TDBProperty> dataCursor = this->GetPropertyRecordForceCreate(t, propertyID);
  356.     (this->Transaction()->GetDBPropertyUpdatePointer(dataCursor))->SetLongData(t, newValue);
  357. } // TDBElement::AddLongwordProperty
  358.  
  359. //--------------------------------------------------------------------------------
  360. // TDBElement::AddTypedProperty
  361. //--------------------------------------------------------------------------------
  362. void TDBElement::AddTypedProperty(TTransaction* t, long propertyID, const TAbstractDataReference& data)
  363. {
  364.     AConst<TDBProperty> dataCursor = this->GetPropertyRecordForceCreate(t, propertyID);
  365.     (this->Transaction()->GetDBPropertyUpdatePointer(dataCursor))->SetTypedData(t, data);
  366. } // TDBElement::AddTypedProperty
  367.  
  368. //--------------------------------------------------------------------------------
  369. // TObjectRecordComparisonObject::TestObject
  370. //--------------------------------------------------------------------------------
  371. CompareEnumeration TObjectRecordComparisonObject::TestObject(TTransaction* t, AConst<TDBRecord> testObject)
  372. {
  373.     CompareEnumeration testResult;
  374.  
  375.     AConst<TDBProperty> nameCursor = testObject->DBElementCursor()->GetPropertyRecord(t, kNameProperty);
  376.     if(nameCursor.Exists())
  377.     {
  378.         //
  379.         // The relationship we want to test for is search key with the name cursor,
  380.         // but we must do the test in the reverse order (name cursor with search key)
  381.         // because the data reference for the name cursor is unavailable to us (private).
  382.         // Therefore, we do the test we can and then negate it.
  383.         //
  384.         testResult = (CompareEnumeration)(-((int)nameCursor->Compare(t, fSearchKey)));
  385.     }
  386.     //
  387.     // If the name of the item is empty, then it is equal
  388.     // to the search key if the search key is empty, and
  389.     // it is less than the search key if the search key has
  390.     // any data.
  391.     //
  392.     else
  393.         testResult = fSearchKey.DataLength() == 0 ? kObjectKeysEqual : kSecondObjectComesBefore;
  394.         // testResult = fSearchKey.Compare(TConstDataReference('TEXT', 0, (Ptr)&fSearchKey));
  395.         
  396.     return testResult;
  397. } // TObjectRecordComparisonObject::TestObject
  398.  
  399.